
/****************************************************************************/

void kernelMultitaskerWait(unsigned timerTicks)
{
	// This function will put a process into the waiting state for *at least*
	// the specified number of timer ticks, and yield control back to the
	// scheduler

	// Make sure multitasking has been enabled
	if (!multitaskingEnabled)
		// We can't wait if we're not multitasking yet
		return;
  
	// Don't do this inside an interrupt
	if (kernelProcessingInterrupt)
		kernelPanic("Cannot wait() inside interrupt handler");

	// Make sure the current process isn't NULL
	if (kernelCurrentProcess == NULL)
		// Can't return an error code, but we can't perform the specified
		// action either
		return;

	kernelPerformanceCounterIncI(pcWaitCalls, 1);

	ACQUIRE_LOCK_PROCESS_QUEUE();

	// Set the current process to "waiting"
	kernelCurrentProcess->state = proc_waiting;

	// Set the wait until time
	kernelCurrentProcess->waitUntil = (kernelSysTimerRead() + timerTicks);
	kernelCurrentProcess->waitForProcess = 0;

	RELEASE_LOCK_PROCESS_QUEUE();

	kernelMultitaskerYield();

	return;
}

/****************************************************************************/

int kernelMultitaskerBlock(int processId)
{
	// This function will put a process into the waiting state
	// until the requested blocking process has completed

	int status = 0;
	kernelProcess *blockProcess = NULL;

	// Make sure multitasking has been enabled
	if (!multitaskingEnabled)
		// We can't yield if we're not multitasking yet
		return (status = ERR_NOTINITIALIZED);

	// Don't do this inside an interrupt
	if (kernelProcessingInterrupt)
		kernelPanic("Cannot block() inside interrupt handler");

	// Make sure the current process isn't NULL
	if (kernelCurrentProcess == NULL)
	{
		kernelError(kernel_error, "Can't determine the current process");
		return (status = ERR_BUG);
	}

	kernelPerformanceCounterIncI(pcBlockCalls, 1);

	ACQUIRE_LOCK_PROCESS_QUEUE();

	// Get the process that we're supposed to block on
	blockProcess = getProcessById(processId);
	if (blockProcess == NULL)
	{
		// The process does not exist.
		RELEASE_LOCK_PROCESS_QUEUE();
		kernelError(kernel_error, "The process on which to block does not exist");
		return (status = ERR_NOSUCHPROCESS);
	}

	// Take the text streams that belong to the current process and
	// give them to the target process
	kernelMultitaskerDuplicateIO(kernelCurrentProcess->processId, processId, 0); // don't clear

	// Set the wait for process values
	kernelCurrentProcess->waitForProcess = processId;
	kernelCurrentProcess->waitUntil = NULL;

	// Set the current process to "waiting"
	kernelCurrentProcess->state = proc_waiting;

	RELEASE_LOCK_PROCESS_QUEUE();

	kernelMultitaskerYield();

	// Get the exit code from the process
	return (kernelCurrentProcess->blockingExitCode);
}

/****************************************************************************/

int kernelMultitaskerDetach(void)
{
	int status = 0;
	kernelProcess *parentProcess = NULL;

	// This will allow a program or daemon to detach from its parent process
	// if the parent process is blocking.
	// Make sure multitasking has been enabled
	if (!multitaskingEnabled)
		// We can't yield if we're not multitasking yet
		return (status = ERR_NOTINITIALIZED);

	// Make sure the current process isn't NULL
	if (kernelCurrentProcess == NULL)
	{
		kernelError(kernel_error, "Can't determine the current process");
		return (status = ERR_BUG);
	}

	ACQUIRE_LOCK_PROCESS_QUEUE();

	parentProcess = getProcessById(kernelCurrentProcess->parentProcessId);

	// Set the input/output streams to the console
	kernelMultitaskerDuplicateIO(KERNELPROCID, kernelCurrentProcess->processId, 0); // don't clear  

	// Get the process that's blocking on this one, if any
	if (parentProcess)
	{
		if (parentProcess->waitForProcess == kernelCurrentProcess->processId)
		{
			// Clear the return code of the parent process
			parentProcess->blockingExitCode = 0;

			// Clear the parent's wait for process value
			parentProcess->waitForProcess = 0;

			// Make it runnable
			parentProcess->state = proc_ready;
		}
	}

	RELEASE_LOCK_PROCESS_QUEUE();

	return (status = 0);
}

/****************************************************************************/

